//-------------------------------------------------------------------
//
//  COPYRIGHT (C) 2019, 
//
//  THIS FILE MAY NOT BE MODIFIED OR REDISTRIBUTED WITHOUT THE
//  EXPRESSED WRITTEN CONSENT OF INSPUR
//-------------------------------------------------------------------
// Title       : fifo.v
// Author      : 
// Created     : 04/10/2019
// Description : self designed fifo
// Modification History:
// V1.0: 2019.04.10, first created by 
//-------------------------------------------------------------------
module fifo(/*autoarg*/
   // Outputs
   fifo_out, fifo_usedw, fifo_empty ,
   // Inputs
   clk, rst_n, fifo_clr, fifo_we, fifo_in, fifo_re
   );

parameter WIDTH = 64;
parameter ADDR = 3;
parameter FIFO_LATENCY = 1 ;
parameter MAX = 1<<ADDR ;
input clk ;
input rst_n ; 
input fifo_clr;
input fifo_we ; 
input [WIDTH-1:0] fifo_in;
input fifo_re ;
output [WIDTH-1:0] fifo_out ;
output          fifo_empty ;
output [ADDR:0] fifo_usedw ;
wire [WIDTH-1:0] fifo_out  ;
reg [WIDTH-1:0] fifo_out_wire ;
reg [ADDR-1:0] wrptr ;
reg [ADDR-1:0] rdptr ;
wire [ADDR-1:0] wrptr_nxt = (fifo_clr?{(ADDR){1'b0}}:( fifo_we ?( ( wrptr==MAX-1'b1) ? {(ADDR){1'b0}} :  (wrptr+1'b1 )) : wrptr ))  ;
wire [ADDR-1:0] rdptr_nxt = (fifo_clr?{(ADDR){1'b0}}:( fifo_re ?( ( rdptr==MAX-1'b1) ? {(ADDR){1'b0}} :  (rdptr+1'b1 )) : rdptr ))  ;

always @ ( posedge clk or negedge rst_n )
begin
	if (~rst_n)
	begin
		/*AUTORESET*/
		// Beginning of autoreset for uninitialized flops
		rdptr <= {ADDR{1'b0}};
		wrptr <= {ADDR{1'b0}};
		// End of automatics
	end
	else
	begin
		wrptr <= wrptr_nxt ;
		rdptr <= rdptr_nxt ;
	end
end

reg [ADDR:0] fifo_usedw_nxt ;
reg [ADDR:0] fifo_usedw ;

always @(*)
begin
		case ( {fifo_we,fifo_re} )
		2'b10: fifo_usedw_nxt = fifo_usedw + 1'b1 ;
		2'b01:fifo_usedw_nxt = fifo_usedw - 1'b1 ;
		default:fifo_usedw_nxt = fifo_usedw  ;
		endcase
end

always @ ( posedge clk or negedge rst_n )
begin
	if (~rst_n)
	begin
		/*AUTORESET*/
		// Beginning of autoreset for uninitialized flops
		fifo_usedw <= {(1+(ADDR)){1'b0}};
		// End of automatics
	end
	else
	begin
		fifo_usedw <= fifo_clr?0:fifo_usedw_nxt ;
	end
end
wire fifo_empty = (fifo_usedw == {(ADDR+1){1'b0}}) ;
reg [WIDTH-1:0] fifo_in_reg[MAX-1:0] ;
integer ii ;
always @ ( posedge clk  )
begin
	if (fifo_we)
	begin
		fifo_in_reg[wrptr] <= fifo_in ;
	end
end

reg [WIDTH-1:0] fifo_content;
reg [WIDTH-1:0] fifo_content_wire;

always @ ( *  )
begin
	if (fifo_re)
	    fifo_out_wire = fifo_content_wire ;
	else
	    fifo_out_wire  = fifo_content ;
end


delay #( WIDTH , FIFO_LATENCY ) u0_delay_fifo   ( clk ,  fifo_out_wire   , fifo_out  );

always @ ( *  )
begin
	if (fifo_empty)
		fifo_content_wire = fifo_in ;
	else
		fifo_content_wire = fifo_in_reg[rdptr] ;
end

always @ ( posedge clk or negedge rst_n )
begin
	if (~rst_n)
	begin
		/*AUTORESET*/
		// Beginning of autoreset for uninitialized flops
		fifo_content <= {WIDTH{1'b0}};
		// End of automatics
	end
	else if (fifo_clr)
		fifo_content <= {WIDTH{1'b0}};
	//else if (fifo_re)
	else 
		fifo_content <=  fifo_content_wire ;
end

//synopsys translate_off
wire error = (fifo_usedw > MAX) ;
reg err_exist = 1'b0 ;
reg err_exist_d = 1'b0;
always @ ( posedge clk  )
begin
	err_exist <= (error?1'b1:err_exist) ;
	err_exist_d <= err_exist ;
	if (err_exist&&(~err_exist_d))
		$display("%m,%t,fifo Error",$time) ;
end
//synopsys translate_on

endmodule

